package qingluan


import (
	"fmt"
	"ovs"
	"iptables"
	"strings"
	"errors"
	// "strconv"
	"db"

	log "github.com/sirupsen/logrus"
)


var (
	ovsClietnt = ovs.New(
		ovs.Sudo(),
	)
	modeFlatStat = true
	FvBridge = "ovs-fv"
	portKey = "mapport"
)

type Host struct {
	Name   string
	IP     string
	Ports  []string
}

func (N *Network) WriteEtcd(key string) error {
	if err := db.SetKey(key, N); err != nil {
		return err
	}
	return nil
}


func (N *Network) ReadEtcd(key string) (*Network, error) {
	value, err := db.GetKey(key, &Network{})
	if err != nil {
		return &Network{}, err
	}
	network, ok := value.(*Network)
	if !ok {
		return &Network{}, err
	}
	return network, nil
}


func (N *Network) DeleteKey(key string) error {
	err := db.DelKey(key)
	if err != nil {
		return err
	}
	return nil
}



func (E *Endpoint) DeleteKey(key string) error {
	err := db.DelKey(key)
	if err != nil {
		return err
	}
	return nil
}

func (E *Endpoint) WriteEtcd(key string) error {
	if err := db.SetKey(key, E); err != nil {
		return err
	}
	return nil
}

func (E *Endpoint) ReadEtcd(key string) (*Endpoint, error) {
	value, err := db.GetKey(key, &Endpoint{})
	if err != nil {
		return &Endpoint{}, err
	}
	endpoint, ok := value.(*Endpoint)
	if !ok {
		return &Endpoint{}, err
	}
	return endpoint, nil
}

func (driver *QingluanNet) createBridge(model, networkID, gateway, vni string) error {
	ovsBridgeName := "ovs-" + networkID[:5]
	switch model {
		case modeNAT:
			log.Debugf("initNat")
			// Add ovs bridge 
			if err := ovsClietnt.VSwitch.AddBridge(ovsBridgeName); err != nil {
				log.Fatalf("failed to add bridge: %v", err)
				return err
			}
			// Set ovs bridge Ip and Up
			if err := ovsClietnt.VSwitch.SetBridgeIp(ovsBridgeName, gateway); err != nil {
				log.Fatalf("failed to set bridge IP: %v", err)
				return err
			}
			// Add NAT rules for iptables
			if err := iptables.EnableNAT(gateway) ; err != nil {
				log.Fatalf("Could not set NAT rules for bridge %s", ovsBridgeName)
				return err
			}

		case modeNoNAT:
			log.Debugf("initNonat")
			// Add ovs bridge 
			if err := ovsClietnt.VSwitch.AddBridge(ovsBridgeName); err != nil {
				log.Fatalf("failed to add bridge: %v", err)
				return err
			}
			// Set ovs bridge Ip
			if err := ovsClietnt.VSwitch.SetBridgeIp(ovsBridgeName, gateway); err != nil {
				log.Fatalf("failed to set bridge IP: %v", err)
				return err
			}
		case modeFlat:
			log.Debugf("flat")
			// Add ovs bridge 
			if err := ovsClietnt.VSwitch.AddBridge(ovsBridgeName); err != nil {
				log.Fatalf("failed to add bridge: %v", err)
				return err
			}
			// Set ovs bridge connection master bridge
			if err := ovsClietnt.VSwitch.BridgeConnBridge(ovsBridgeName, FvBridge); err != nil {
				log.Fatalf("failed to connection bridge: %v", err)
				return err
			}
		case modeVlan:
			log.Debugf("vlan")
			// Add ovs bridge 
			if err := ovsClietnt.VSwitch.AddBridge(ovsBridgeName); err != nil {
				log.Fatalf("failed to add bridge: %v", err)
				return err
			}
			// Set ovs bridge Ip & UP
			if err := ovsClietnt.VSwitch.SetBridgeIp(ovsBridgeName, gateway); err != nil {
				log.Fatalf("failed to set bridge IP: %v", err)
				return err
			}
			// Set ovs bridge connection master bridge
			if err := ovsClietnt.VSwitch.BridgeConnBridge(ovsBridgeName, FvBridge); err != nil {
				log.Fatalf("failed to connection bridge: %v", err)
				return err
			}
			// set ovs bridge port vni value 
			if err := ovsClietnt.VSwitch.SetPortVlanID(ovsBridgeName, vni); err != nil {
				log.Fatalf("failed to set  port vni: %v", err)
				return err
			}
		case modeVxlan:
			log.Debugf("vxlan")
			// Add ovs bridge 
			if err := ovsClietnt.VSwitch.AddBridge(ovsBridgeName); err != nil {
				log.Fatalf("failed to add bridge: %v", err)
				return err
			}
			// Set ovs bridge Ip & UP
			if err := ovsClietnt.VSwitch.SetBridgeIp(ovsBridgeName, gateway); err != nil {
				log.Fatalf("failed to set bridge IP: %v", err)
				return err
			}
		default:
			log.Debugf("err network model.")
			return errors.New("err network model.")
	}
	return nil
}

func (driver *QingluanNet) LinkSetMaster(brige, peer string) error {
	if err := ovsClietnt.VSwitch.AddPort(brige, peer); err != nil {
		return err
	}
	return nil
}

func deleteBridge(networkID string) error {
	ovsBridgeName := "ovs-" + networkID[:5]
	if err := ovsClietnt.VSwitch.DeleteBridge(ovsBridgeName); err != nil {
		log.Fatalf("failed to delete bridge: %v", err)
		return err
	}
	return nil
}

func deletePort (bridge, port string) error {
	if err := ovsClietnt.VSwitch.DeletePort(bridge, port); err != nil {
		log.Fatalf("failed to delete bridge: %v", err)
		return err
	}
	return nil
}

func (H *Host) WriteEtcd(key string) error {
	if err := db.SetKey(key, H); err != nil {
		return err
	}
	return nil
}

func (H *Host) ReadEtcd(key string) (*Host, error) {
	value, err := db.GetKey(key, &Host{})
	if err != nil {
		return &Host{}, err
	}
	host, ok := value.(*Host)
	if !ok {
		return &Host{}, err
	}
	return host, nil
}

func (H *Host) AddPort(key, port string) error {
	var err error
	host := &Host{Ports: make([]string, 0, 1)}
	if host, err = H.ReadEtcd(key); err != nil {
		return err
	}
	host.Ports = append(host.Ports, port)
	if err = host.WriteEtcd(key); err != nil {
		return err
	}
	return nil
}

func (H *Host) GetPort(key, port string) ([]string, error) {
	var err error
	host := &Host{Ports: make([]string, 0, 1)}
	if host, err = H.ReadEtcd(key); err != nil {
		return host.Ports, err
	}
	return host.Ports, nil
}

func (H *Host) PortIsExist(key, port string) (bool, error) {
	var err error
	host := &Host{Ports: make([]string, 0, 1)}
	if host, err = H.ReadEtcd(key); err != nil {
		return false, err
	}
	for _, eachItem := range host.Ports {
		if eachItem == port {
			return true, nil
		}
	}
	return false, nil
}
func (H *Host) Delport(key string, ports []string) error {
	H.Ports = DeleteStrSliceElms(H.Ports, ports)
	if err := H.WriteEtcd(key); err != nil {
		return err
	}
	return nil
}

func mapPort(conIp, networkID string, ports []map[string]string) error {
	var hostPort, conPort string
	host := &Host{}
	if !db.IsKeyExist(portKey) {
		host.WriteEtcd(portKey)
	}
	for _, value1 := range(ports) {
		for key, value := range(value1){
			if key == HostPort {
				hostPort = value
			}else if key == Port {
				conPort = value
			}
		}
		ok, err := host.PortIsExist(portKey, hostPort)
		if err != nil {
			return err
		}
		if !ok{
			if err := iptables.MapPort(conIp, conPort, hostPort); err != nil {
				return err
			}
			host.AddPort(portKey, hostPort)
		}else{
			return errors.New("Port is already allocated.")
		}
	}
	return nil
}

func delMapPort(networkId, endpointId string) error {
	var conPort, hostPort string
	endpoint := &Endpoint{}
	endpoint, _ = endpoint.ReadEtcd(fmt.Sprintf("endpoint/%s",endpointId))
	address, maPort :=  endpoint.Address, endpoint.MaPort
	containerIp  := strings.Split(address, "/")[0]
	for _, value1 := range(maPort) {
		for key, value := range(value1){
			if key == HostPort {
				hostPort = value
			}else if key == Port {
				conPort = value
			}
		}
		if err := iptables.DelMapPort(containerIp, conPort, hostPort); err != nil {
			return err
		}
    }
	return nil
}

// delete ele in s1
func DeleteStrSliceElms(sl []string, s2 []string) []string {
	if len(sl) == 0 || len(s2) == 0 {
		return sl
	}
	m := make(map[string]struct{})
	for _, v := range s2 {
		m[v] = struct{}{}
	}
	res := make([]string, 0, len(sl))
	for _, v := range sl {
		if _, ok := m[v]; !ok {
			res = append(res, v)
		}
	}
	return res
}
